package com.fxu.framework.core.sql;

import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.fxu.framework.core.util.FieldUtil;
import lombok.ToString;
import org.apache.ibatis.reflection.property.PropertyNamer;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * 可以序列化的字段类
 * 用于SQuery中的显示字段，查询字段，级联字段等
 *
 * @author fangxu
 * @version 1.0.0
 */
@ToString
public class SField<T> implements Serializable {
    /**
     * 字段归属的类
     */
    private Class<T> clazz;

    /**
     * 名字
     */
    private final String name;

    /**
     * 名字
     */
    private SFunc<T, ?> func;

    /**
     * 使用对象方法初始化
     * User::getId
     * @param clazz Entity类
     * @param func 对象方法
     */
    public SField(Class<T> clazz, SFunc<T, ?> func) {
        if (func == null) {
            throw new RuntimeException("SField初始化错误:func is null!");
        }
        this.clazz = clazz;
        this.func = func;
        this.name = getName(clazz, func);
    }

    /**
     * 使用对象属性初始化
     * ReflectUtil.getField(User.class, "id")
     * @param field 对象属性
     */
    @SuppressWarnings("unchecked")
    public SField(Field field) {
        if (field == null) {
            throw new RuntimeException("SField初始化错误:field is null!");
        }
        this.clazz = (Class<T>) field.getDeclaringClass();
        this.name = field.getName();
    }

    /**
     * 使用对象属性初始化
     * ReflectUtil.getField(User.class, "id")
     * @param clazz Entity类
     * @param field 对象属性
     */
    public SField(Class<T> clazz, String field) {
        if (clazz == null) {
            throw new RuntimeException("SField初始化错误:clazz is null!");
        }
        if (!ReflectUtil.hasField(clazz, field)) {
            throw new RuntimeException("类{"+clazz+"}不存在属性:{"+field+"}");
        }
        Field temp = ReflectUtil.getField(clazz, field);
        this.clazz = clazz;
        this.name = temp.getName();
    }

    private static ConcurrentMap<SFunc<?, ?>, String> columnMap = new ConcurrentHashMap<>();

    /**
     * 获取SFunc对应的Field名字
     * @param clazz Entity类
     * @param column Entity的方法
     * @param <T> Entity类的约束
     * @return 返回字段名
     */
    public static <T> String getName(Class<T> clazz, SFunc<T, ?> column) {
        SLambda lambda = SLambda.extractByCache(column);
        if (lambda.getImplClassName().equals(SQuery.class.getName())) {
            return FieldUtil.getPriKeyName(clazz);
        }
        // this.clazz = lambda.getImplClass();
        if (columnMap.get(column) == null) {
            String fieldName = PropertyNamer.methodToProperty(lambda.getImplMethodName());
            if (StrUtil.isEmpty(fieldName) || !ReflectUtil.hasField(lambda.getImplClass(), fieldName)) {
                throw new RuntimeException(StrUtil.format("类 {} 不含方法 {}", lambda.getImplClass(), fieldName));
            }
            columnMap.put(column, fieldName);
        }
        return columnMap.get(column);
    }

    public String getName() {
        return name;
    }

    public Class<?> getClazz() {
        return clazz;
    }

    public SFunc<T, ?> getFunc() {
        return func;
    }

    public String getColumnName() {
        return FieldUtil.getColumnByBean(clazz, name);
    }
}